package controllers

import (
    "gouniapp/configs"
    "encoding/json"
    "fmt"
    "net/http"
    "github.com/gin-gonic/gin"
    lzstring "github.com/Lazarus/lz-string-go"
    "strconv"
    "runtime/debug"
    "sync/atomic"
    "log"
    "strings"
    "time"
    "sync"
    "io"
)

type Plcfdata struct {
    Bm      string  `json:"bm"`     //表名
    Yzm     bool    `json:"yzm"`    //是否加验证码
}

//成绩
type Student struct {
    YW    string `json:"YW"`
    SX    string `json:"SX"`
    WY    string `json:"WY"`
    SXKM  string `json:"SXKM"`
    ZXKM1 string `json:"ZXKM1"`
    ZXKM2 string `json:"ZXKM2"`
    SXKMMC  string `json:"SXKMMC"`
    ZXKM1MC string `json:"ZXKM1MC"`
    ZXKM2MC string `json:"ZXKM2MC"`
    JF    string `json:"JF"`
    TZF   string `json:"TZF"`
    PM    string `json:"PM"`
}

//录取情况{"yxmc":"中国人民公安大学","zymc":"公安管理学","pcmc":"提前本科类","jhmc":"非定向","lqsj":"2024-07-15T00:00:00","yxdh":"0003","pcdm":"0","kldm":"1","jhxzdm":"0","lqzt":"录取","sfzh":"0020","zydh":"09","zyzmc":"第103组"}
type Luquzt struct {
    Yxmc    string `json:"yxmc"`    //院校名称
    Zymc    string `json:"zymc"`    //专业名称
    Pcmc    string `json:"pcmc"`    //批次
    Jhmc    string `json:"jhmc"`    //定向
    Lqsj    string `json:"lqsj"`    //录取时间
    Lqzt    string `json:"lqzt"`    //录取状态
    Zyzmc    string `json:"zyzmc"`    //专业组名称
}

const (
	concurrency = 5     // 并发数
	batchSize   = 10    // 批次大小
	retryLimit  = 3     // 重试次数
	maxRuneLen  = 4     // 需要截取的最后字符数
)
	
//批量查分，并发处理的 goroutine 数量为5，同时最多会有5个goroutine在处理数据，每个goroutine中处理10条数据。
func PlcfApi(ctx *gin.Context) {
	// -------------------- 初始化校验 --------------------
	var jsons Plcfdata
	if err := ctx.ShouldBindJSON(&jsons); err != nil {
		log.Printf("JSON绑定失败: %v", err)
		JsonResponse(ctx, http.StatusBadRequest, "请求参数格式错误", nil)
		return
	}
	if jsons.Bm == "" {
		JsonResponse(ctx, http.StatusBadRequest, "表名为空", nil)
		return
	}
	// -------------------- 数据准备 --------------------
	var data []map[string]interface{}
	if err := configs.DB.Table(jsons.Bm).Where("K3 = ?", "").Find(&data).Error; err != nil {
		log.Printf("数据库查询失败: %v", err)
		JsonResponse(ctx, http.StatusInternalServerError, "数据查询失败", nil)
		return
	}
	if len(data) == 0 {
		JsonResponse(ctx, http.StatusOK, "没有需要处理的数据", nil)
		return
	}
	ocrurl := configs.Config.GetString("AI.ocrurl")
	if ocrurl == "" {
		JsonResponse(ctx, http.StatusInternalServerError, "OCR服务未配置", nil)
		return
	}
	// -------------------- 任务分发 --------------------
	taskCh := make(chan []map[string]interface{}, concurrency)
	go func() {
		defer close(taskCh)
		for i := 0; i < len(data); i += batchSize {
			end := i + batchSize
			if end > len(data) {
				end = len(data)
			}
			// 创建批次副本
			batch := make([]map[string]interface{}, end-i)
			for j := i; j < end; j++ {
				batch[j-i] = copyMap(data[j])
			}
			taskCh <- batch
		}
	}()
	// -------------------- 并发处理 --------------------
	var (
		wg      sync.WaitGroup
		success uint32 // 原子计数器
		fail    uint32
	)
	for i := 0; i < concurrency; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			defer func() {
				if r := recover(); r != nil {
					log.Printf("协程panic: %v\n%s", r, debug.Stack())
				}
			}()
			for task := range taskCh {
				for _, record := range task {
					processRecord(record, ocrurl, jsons, retryLimit, &success, &fail)
				}
			}
		}()
	}
	wg.Wait()
	JsonResponse(ctx, http.StatusOK, "操作完成", gin.H{"success": success,"fail":fail,})
}

// -------------------- 辅助函数 --------------------
func copyMap(m map[string]interface{}) map[string]interface{} {
	copyMap := make(map[string]interface{}, len(m))
	for k, v := range m {
		copyMap[k] = v
	}
	return copyMap
}

func getStringField(m map[string]interface{}, key string) (string, bool) {
	val, exists := m[key]
	if !exists {
		return "", false
	}
	s, ok := val.(string)
	if !ok || s == "" {
		return "", false
	}
	return s, true
}

func processRecord(record map[string]interface{}, ocrurl string, jsons Plcfdata, retryLimit int, success, fail *uint32) {
	// 关键字段校验
	k0, ok := getStringField(record, "K0")
	if !ok {
		log.Printf("缺失K0字段: %+v", record)
		atomic.AddUint32(fail, 1)
		return
	}
	k2, ok := getStringField(record, "K2")
	if !ok {
		log.Printf("缺失K2字段: %+v", record)
		atomic.AddUint32(fail, 1)
		return
	}
	// 安全处理K2
	runes := []rune(k2)
	if len(runes) < maxRuneLen {
		log.Printf("K2长度不足%d字符: %s", maxRuneLen, k2)
		atomic.AddUint32(fail, 1)
		return
	}
	sfzh4 := string(runes[len(runes)-maxRuneLen:])
	// 重试逻辑
	var lastErr error
	for num := 0; num < retryLimit; num++ {
		// 请求OCR服务
		ksh := compressToBase64(k0)
		sfz := compressToBase64(sfzh4)
		cj, lq := getcj(ksh, sfz, jsons.Yzm, ocrurl)
		time.Sleep(200 * time.Millisecond)
		// 处理成绩数据
		if cj == "" {
			lastErr = fmt.Errorf("空成绩响应")
			continue
		}
		var student Student
		if err := json.Unmarshal([]byte(cj), &student); err != nil {
			lastErr = fmt.Errorf("成绩解析失败: %w", err)
			continue
		}
		// 更新记录
		updateFields := map[string]interface{}{
			"K3":  strings.ReplaceAll(student.YW, " ", ""),
			"K4":  strings.ReplaceAll(student.SX, " ", ""),
			"K5":  strings.ReplaceAll(student.WY, " ", ""),
			"K6":  strings.ReplaceAll(student.SXKMMC+"-"+student.SXKM, " ", ""),
			"K7":  strings.ReplaceAll(student.ZXKM1MC+"-"+student.ZXKM1, " ", ""),
			"K8":  strings.ReplaceAll(student.ZXKM2MC+"-"+student.ZXKM2, " ", ""),
			"K9":  strings.ReplaceAll(student.JF, " ", ""),
			"K10": strings.ReplaceAll(student.TZF, " ", ""),
			"K11": strings.ReplaceAll(student.PM, " ", ""),
		}
		// 处理录取信息
		if lq != "" {
			var lqqk Luquzt
			if err := json.Unmarshal([]byte(lq), &lqqk); err == nil {
				updateFields["K12"] = strings.ReplaceAll(lqqk.Yxmc, " ", "")
				updateFields["K13"] = strings.ReplaceAll(lqqk.Zymc, " ", "")
				updateFields["K14"] = strings.ReplaceAll(lqqk.Pcmc, " ", "")
				updateFields["K15"] = strings.ReplaceAll(lqqk.Jhmc, " ", "")
				updateFields["K16"] = strings.ReplaceAll(lqqk.Lqsj, " ", "")
				updateFields["K17"] = strings.ReplaceAll(lqqk.Lqzt, " ", "")
				updateFields["K18"] = strings.ReplaceAll(lqqk.Zyzmc, " ", "")
			}
		}
		// 数据库更新
		if id, exists := record["id"]; exists {
			if err := configs.DB.Table(jsons.Bm).
				Where("id = ?", id).
				Updates(updateFields).Error; err != nil {
				lastErr = fmt.Errorf("数据库更新失败: %w", err)
				continue
			}
			atomic.AddUint32(success, 1)
			return
		}
		lastErr = fmt.Errorf("记录缺少主键字段")
	}
	log.Printf("处理失败: %v | 记录: %+v", lastErr, record)
	atomic.AddUint32(fail, 1)
}

func getcj(ksh, sfz string, isyzm bool, ocrurl string) (cj, lq string)  {
    // 发起第一次访问并获取 Cookie并获取验证码
    resp, err := http.Get("http://gkcf.jxedu.gov.cn/captcha/getcode?t=" + strconv.FormatInt(time.Now().Unix(), 10))
    if err != nil {
        fmt.Println("Error getting initial cookie:", err)
        return
    }
    defer resp.Body.Close()
    // 从响应头中获取 Set-Cookie 信息
    setCookie := resp.Header.Get("Set-Cookie")
    cookieParts := strings.Split(setCookie, ";")
    mycookie := cookieParts[0]
    fmt.Println("cookie", mycookie)
    body, err := io.ReadAll(resp.Body)
    if err != nil {
    	fmt.Println("Error reading response body:", err)
    	return
    }
    var post3 map[string]interface{}
    err = json.Unmarshal(body, &post3)
    if err != nil {
    	fmt.Println("Error parsing JSON:", err)
    	return
    }
    data, ok := post3["Data"].(map[string]interface{})
    if !ok {
        fmt.Println("Data 字段不是map[string]interface{}类型")
        return
    } else {
        if isyzm == false {
            cj, lq = getcjpost(ksh, sfz, "", mycookie)
        }else{
            yzm := getyzm(data, ocrurl)
            if yzm != "" {
                //fmt.Println("key1=" + ksh + "&key2=" + sfz + "&key3=" + yzm)
                cj, lq = getcjpost(ksh, sfz, yzm, mycookie)
            }
        }
        return
    }
}

//识别验证码
func getyzm(data map[string]interface{}, ocrurl string) string{
    yzm, ok := data["Img"].(string)
    if !ok {
        fmt.Println("Img字段不是字符串类型")
        return ""
    } else {
        yzmResp, err := http.Post(ocrurl, "application/x-www-form-urlencoded", strings.NewReader("img="+yzm))
        if err != nil {
        	fmt.Println("Error getting captcha result:", err)
        	return ""
        }
        defer yzmResp.Body.Close()
        yzmBody, err := io.ReadAll(yzmResp.Body)
        if err != nil {
        	fmt.Println("Error reading captcha result body:", err)
        	return ""
        }
        yzm = string(yzmBody)
        fmt.Println("识别验证码", yzm)
        yzm = compressToBase64(yzm)
        return yzm
    }
}

// 发送请求并获取成绩源码
func getcjpost(ksh, sfz, yzm, mycookie string) (cj, lq string) {
    reqBody := strings.NewReader("key1=" + ksh + "&key2=" + sfz + "&key3=" + yzm)
    req, err := http.NewRequest("POST", "http://gkcf.jxedu.gov.cn/", reqBody)
    if err != nil {
    	fmt.Println("Error creating request:", err)
    	return
    }
    req.Header.Set("cookie", mycookie)
    req.Header.Set("content-type", "application/x-www-form-urlencoded")
	req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0")
    client := &http.Client{}
    resp2, err := client.Do(req)
    if err != nil {
    	fmt.Println("Error sending request:", err)
    	return
    }
    defer resp2.Body.Close()
    body2, err := io.ReadAll(resp2.Body)
    if err != nil {
    	fmt.Println("Error reading response body:", err)
    	return
    }
    time.Sleep(100 * time.Millisecond)
    bodyString := string(body2)
    //fmt.Println("获取成绩", bodyString)
    cj = extractMiddleText(bodyString, "var _score = '", "'.toLowerCase();")
    lq = extractMiddleText(bodyString, "var _luqu = '", "';") //录取 {"zkzh":"16073103911","xm":"彭紫谦","ksh":"24360731110105","yxmc":"中国人民公安大学","zymc":"公安管理学","pcmc":"提前本科类","jhmc":"非定向","lqsj":"2024-07-15T00:00:00","yxdh":"0003","pcdm":"0","kldm":"1","jhxzdm":"0","lqzt":"录取","sfzh":"0020","zydh":"09","zyzmc":"第103组"}'
    return
}

//取中间文本
func extractMiddleText(originalText, leftText, rightText string) string {
	startIndex := strings.Index(originalText, leftText)
	if startIndex == -1 {
		return "" // 如果找不到左边文本，返回空字符串
	}
	startIndex += len(leftText)
	endIndex := strings.Index(originalText[startIndex:], rightText)
	if endIndex == -1 {
		return "" // 如果找不到右边文本，返回空字符串
	}
	return originalText[startIndex : startIndex+endIndex]
}

//批量查分加解密，调用：jmKey := compressToBase64(key)
func compressToBase64(input string) string {
	return lzstring.Compress(input, "")
}